package org.aug.demo.jvm.classpath;

import org.aug.demo.jvm.classpath.impl.WildcardEntry;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;


/**
 * 就用来读取上面提到的三种类路径(CompositeEntry(WildcardEntry),DirEntry,,ZipEntry)了，在构造方法中就解析路径并构造 Entry。
 */
public class Classpath {

    // JVM 启动时必须要加载的三类类路径
    private Entry bootstrapClasspath; //启动类路径
    private Entry extensionClasspath; //扩展类路径
    private Entry userClasspath; //用户类路径

    public Classpath(String jreOption, String cpOption){
        // 启动类和扩展类由 jre 提供
        // 启动类&扩展类“C:\Program Files\Java\jdk1.8.0_161\jre"
        parseBootAndExtensionClasspath(jreOption);

        // 解析用户自定义的类的路径
        //用户类 E：\..\org\itstack\demo\test\HelloWorld
        parseUserClasspath(cpOption);
    }

    /**
     * 两个 parse 方法就是根据传入的参数路径，构造 Entry 用的，很简单，就是个路径的拼接
     */
    private void parseBootAndExtensionClasspath(String jreOption) {
        String jreDir = getJreDir(jreOption);

        // 启动类在 jre/lib/*
        String jreLibPath = Paths.get(jreDir,"lib") + File.separator + "*";
        bootstrapClasspath = new WildcardEntry(jreLibPath);

        // 扩展类在 jre/lib/ext/*
        String jreExtPath = Paths.get(jreDir, "lib", "ext") + File.separator + "*";
        extensionClasspath = new WildcardEntry(jreExtPath);
    }

    private void parseUserClasspath(String cpOption){
        // 如果用户没有提供-classpath/-cp选项，则使用当前目录作为用户类路径
        if(cpOption == null){
            cpOption = ".";
        }
        userClasspath = Entry.create(cpOption);
    }


    /**
     * 寻找 jre 路径顺序
     * 1. 用户指定路径
     * 2. 当前文件夹下的 jre 文件夹
     * 3. 系统环境变量 JAVA_HOME 指定的文件夹
     */
    private static String getJreDir(String jreOption) {
        if (jreOption != null && Files.exists(Paths.get(jreOption))) {
            return jreOption;
        }
        if (Files.exists(Paths.get("./jre"))) {
            return "./jre";
        }
        String jh = System.getenv("JAVA_HOME");
        if (jh != null) {
            return Paths.get(jh, "jre").toString();
        }
        throw new RuntimeException("Can not find JRE folder!");
    }


    /**
     * 依次从启动类路径、扩展类路径和用户类路径中搜索class文件
     *
     */
    public byte[] readClass(String className) throws Exception{
        // 根据类名获取一个类的字节码
        // 根据双亲委派机制，按顺序读取，并且在前两个读取不到时不会报错

        className = className + ".class";

        //启动类
        try {
            return bootstrapClasspath.readClass(className);
        }catch (Exception ignored){
            //ignored
        }

        //扩展类
        try{
            return extensionClasspath.readClass(className);
        } catch (Exception ignored){
            //ignored
        }

        //用户类
        return userClasspath.readClass(className);

    }

}
